package controller;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.JOptionPane;
import javax.swing.JTable;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
import javax.swing.text.DateFormatter;

import model.Belegung;
import model.Kunde;
import model.Rechnung;
import model.Reservierung;
import model.Zimmer;
import model.exceptions.AlreadyCalculatedBelegungenException;
import model.exceptions.NotEnoughFreeRoomsException;

import view.CreateReservationView;

public class GUICreateReservationController extends GUIAbstractController {
	private CreateReservationView view;
	private CustomerTableModel customerTable;
	private CustomerTableModel selectedCustomerTable;
	private RoomResultTabelModel roomResultModel;
	private ArrayList<Kunde> customers;
	private ArrayList<Kunde> selectedCustomers;
	private ArrayList<Zimmer> selectedRooms; 
	private int selectedRoomRow;
	private PersistenceManager persistenceManager;

	public GUICreateReservationController(){
		super();
		
		//no row selected by default
		selectedRoomRow = -1;
		
		persistenceManager = PersistenceManager.getInstance();
		
		System.out.println("Customers: "+ persistenceManager.getAllKunde());
		
		customers = new ArrayList<Kunde>(persistenceManager.getAllKunde());
		for(Kunde k : customers){
			k.setGeburtstag(new Date(0));
		}
		selectedCustomers = new ArrayList<Kunde>();
		selectedRooms = new ArrayList<Zimmer>();
		view = new CreateReservationView();
		view.getSearchForCustomer().addKeyListener(new KeyListener() {
			
			@Override
			public void keyTyped(KeyEvent e) {
				//search for customer and display the result in the result area
				String s = (String) view.getSearchForFields().getSelectedItem();
				String field = view.getSearchByItems().get(s);
				customers = new ArrayList<Kunde>(
						PersistenceManager.getInstance().searchKunde(field, view.getSearchForCustomer().getText()));
				for(Kunde k : customers){
					k.setGeburtstag(new Date(0));
				}
				reloadResultTable();	
			}
			
			@Override
			public void keyReleased(KeyEvent arg0) {
				//no action
				
			}
			
			@Override
			public void keyPressed(KeyEvent arg0) {
				//no action
				
			}
		});
		view.getResultForCustomerSearch().addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
			    if (e.getClickCount() == 2) {
			      JTable target = (JTable)e.getSource();
			      int row = target.getSelectedRow();
			      selectedCustomers.add(customers.get(row));
			      reloadResultTable();
			    }
			  }
		});
		view.getSelectedCustomers().addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
			    if (e.getClickCount() == 2) {
			      JTable target = (JTable)e.getSource();
			      int row = target.getSelectedRow();
			      customers.add(selectedCustomers.get(row));
			      selectedCustomers.remove(row);
			      reloadResultTable();
			    }
			  }
		});
		
		view.getSearchForCustomer().addFocusListener(new FocusListener() {
			
			@Override
			public void focusLost(FocusEvent arg0) {
				view.getSearchForCustomer().setSelectionStart(0);
				view.getSearchForCustomer().setSelectionEnd(0);
				
			}
			
			@Override
			public void focusGained(FocusEvent arg0) {
				view.getSearchForCustomer().setSelectionStart(0);
				view.getSearchForCustomer().setSelectionEnd(
						view.getSearchForCustomer().getText().length());
				
			}
		});
		view.getCancelButton().addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				int o= JOptionPane.showConfirmDialog(null,"Do you really want to cancel the creation of this reservation","Cancel", JOptionPane.YES_NO_OPTION);
				if(o==0){
					view.setVisible(false);
				}
			}
		});
		view.getCreateReservationButton().addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent arg0) {
				if(validateReservation()){
					//create the new reservation
					Reservierung tmpReservierung;
					tmpReservierung = new Reservierung();
					tmpReservierung.setVon(view.getFromSpinnerModel().getDate());
					tmpReservierung.setBis(view.getToSpinnerModel().getDate());
					for(Kunde k : selectedCustomers){
						tmpReservierung.addKunde(k);
					}
					try {
						tmpReservierung.doSimpleAssignRoom(selectedRooms);
					} catch (NotEnoughFreeRoomsException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					} catch (AlreadyCalculatedBelegungenException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
					Rechnung r = new Rechnung(tmpReservierung);
					r.setDatum(tmpReservierung.getBis());
					r.calculatePreis();
					r.setDatum(new Date());
					//Display reservation information for the user to confirm
					String selectedCustomersForReservation ="Customers: \n";
					String period ="For the Period: \n";
					String rooms = "In rooms: \n";
					String price = "Cost: \n   " + r.getPreis();
					//the customers
					for(Kunde k : selectedCustomers){
						selectedCustomersForReservation += "   " + k.getVorname() +" " + k.getNachname() + "\n";
					}
					period += "   From: " + DateFormat.getDateInstance().format(view.getFromSpinnerModel().getDate());
					period += "   To: " + DateFormat.getDateInstance().format(view.getToSpinnerModel().getDate());
					period += "\n";
					//the rooms
					for(Zimmer z : selectedRooms){
						rooms += "   " + z.getName() + "\n";
					}
					
					int o= JOptionPane.showConfirmDialog(null,"Please Confirm Reservation\n" + selectedCustomersForReservation
							+ period + rooms + price,"Confirm", JOptionPane.YES_NO_OPTION);
					
					// delete rechnung, because it is not yet payed!
					tmpReservierung.clearRechnung();
					if(o==0){
						persistenceManager.saveEntity(tmpReservierung);
						view.setVisible(false);
					}
				//if reservation not complete ask the user to complete it
				}else{
					String missing ="";
					for (String s : missingInReservation()){
						missing = missing + s + "\n";
					}
					JOptionPane.showMessageDialog(null,"You are missing the following in your reservation: \n" + missing , "Reservation not Complete", JOptionPane.ERROR_MESSAGE);
				}
				
			}
		});
		
		view.getFromSpinner().addChangeListener(new ChangeListener() {
			
			@Override
			public void stateChanged(ChangeEvent arg0) {
				Date d = view.getFromSpinnerModel().getDate();
				d.setTime(d.getTime() + 1000*60*60*24);
				if(view.getToSpinnerModel().getDate().before(d)){
					view.getToSpinnerModel().setValue(d);
					view.getToSpinner().repaint();	
				}
				reloadResultTable();
			}
		});
		
		view.getToSpinner().addChangeListener(new ChangeListener() {
			
			@Override
			public void stateChanged(ChangeEvent arg0) {
				Date d = view.getToSpinnerModel().getDate();
				d.setTime(d.getTime() - 1000*60*60*24);
				if(view.getFromSpinnerModel().getDate().after(d)){
					view.getFromSpinnerModel().setValue(d);
					view.getFromSpinner().repaint();	
				}
				reloadResultTable();
			}
		});
		
		view.getRoomResultTable().addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
			    if (e.getClickCount() >= 1) {
			      JTable target = (JTable)e.getSource();
			      selectedRoomRow = target.getSelectedRow();
			      selectedRooms = new ArrayList<Zimmer>(roomResultModel.getRoomLists().get(selectedRoomRow));
			    }
			  }
		});
		
		reloadResultTable();
		
	}
	
	private void reloadResultTable() {
		JTable table = view.getResultForCustomerSearch();
		JTable selectedTable = view.getSelectedCustomers();
		JTable roomTable = view.getRoomResultTable();
		customerTable = new CustomerTableModel(customers);
		ArrayList<Zimmer> t = new ArrayList<Zimmer>();
		if(selectedRoomRow >= 0){
			t.addAll(roomResultModel.getRoomLists().get(selectedRoomRow));
		}
		selectedCustomerTable = new CustomerTableModel(selectedCustomers);
		roomResultModel = new RoomResultTabelModel(Zimmer.findSuitableRooms(selectedCustomerTable.getCustomers(), view.getFromSpinnerModel().getDate(), view.getToSpinnerModel().getDate()));

		System.out.println("Customers: " + customers);
		System.out.println("Selected Customers" + selectedCustomers);
		
		customers.removeAll(selectedCustomers);
		customerTable.setCustomers(customers);
		
		table.setModel(customerTable);
		
		selectedTable.setModel(selectedCustomerTable);
		
		roomTable.setModel(roomResultModel);
		
		table.repaint();
		selectedTable.repaint();
		roomTable.repaint();
		if(selectedRoomRow>=0){
			ArrayList<Zimmer> s = new ArrayList<Zimmer>(roomResultModel.getRoomLists().get(selectedRoomRow));
			if(s.size() == t.size()){
				Boolean eq = true;
				for(int j = 0; j < s.size(); j++){
					eq = eq & s.get(j).getName().equals(t.get(j).getName());
				}
				if(eq){
					roomTable.addRowSelectionInterval(selectedRoomRow, selectedRoomRow);
				}
			}
		}
	}
	
	public List<String> missingInReservation(){ 
		ArrayList<String> r = new ArrayList<String>();
		if(selectedCustomers.size() == 0){
			r.add("Please select at least one customer");
		}
		if(selectedRooms.size() == 0){
			r.add("Please choose room(s) for the reservation");
		}
		return r;
	}
	
	public boolean validateReservation(){
		return missingInReservation().size() == 0; 
	}
	
	
	public CreateReservationView getView() {
		return view;
	}

	public void setView(CreateReservationView view) {
		this.view = view;
	}
	
	/*
	 * ############## Private table model class ###############
	 */
	
	private class RoomResultTabelModel extends AbstractTableModel {
	
		private static final long serialVersionUID = 1L;
		private List<List<Zimmer>> rooms;
		public final String[] HeaderNames = { "Room(s)", "Price"};

		public RoomResultTabelModel(List<List<Zimmer>> roomLists){
			rooms = roomLists;
		}
		
		public int getColumnCount() {
			return 2;
		}

		public int getRowCount() {
			return rooms.size();
		}
		
		@Override
		public String getColumnName(int column) {
			return HeaderNames[column];
		};

		public Object getValueAt(int row, int col) {
			
			List<Zimmer> zs = rooms.get(row);
			if (col == 0) {
				String names = "";
				for(Zimmer z : zs){
					names += z.getName() + " ,";
				}
				if(names.length() >= 2){
					names = names.substring(0, names.length()-2);
				}
				return names;
			}
			
			//Calculate the price for a room or a series of rooms
			double price = 0;
			Reservierung tmpReservierung;
			if(selectedCustomers.size() == 0){
				System.out.println("Customer size = 0");
				
				Kunde ku = new Kunde();
				ku.setGeburtstag(new Date(0));

				tmpReservierung = new Reservierung();
				tmpReservierung.setVon(view.getFromSpinnerModel().getDate());
				tmpReservierung.setBis(view.getToSpinnerModel().getDate());
				tmpReservierung.addKunde(ku);
				try {
					tmpReservierung.doSimpleAssignRoom(rooms.get(row));
				} catch (NotEnoughFreeRoomsException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				} catch (AlreadyCalculatedBelegungenException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
				
				tmpReservierung.setRechnung(new Rechnung(tmpReservierung));
				tmpReservierung.getRechnung().setDatum(tmpReservierung.getBis());
				tmpReservierung.getRechnung().calculatePreis();
				tmpReservierung.getRechnung().setDatum(new Date());
				price = tmpReservierung.getRechnung().getPreis();
			}else{
//				ArrayList<Belegung> tmpBelegungen = new ArrayList<Belegung>();
//				int addedtoRoom = 0;
//				int roomIndex = 0;
//				Belegung currentBelegung = new Belegung();
//				currentBelegung.setZimmer(rooms.get(row).get(roomIndex));
//				tmpBelegungen.add(currentBelegung);
//				for(int i = 0; i < selectedCustomers.size();i++){
//					if(addedtoRoom == currentBelegung.getZimmer().getMaxBelegung()){
//						addedtoRoom = 0;
//						roomIndex++;
//						currentBelegung = new Belegung();
//						currentBelegung.setZimmer(rooms.get(row).get(roomIndex));
//						tmpBelegungen.add(currentBelegung);
//					}
//					currentBelegung.addKunde(selectedCustomers.get(i));
//				}
//				
				//tmpReservierung = new Reservierung(tmpBelegungen, 0.0, view.getFromSpinnerModel().getDate(), view.getToSpinnerModel().getDate());
				tmpReservierung = new Reservierung();
				tmpReservierung.setVon(view.getFromSpinnerModel().getDate());
				tmpReservierung.setBis(view.getToSpinnerModel().getDate());
				for (Kunde kk : selectedCustomers){
					tmpReservierung.addKunde(kk);
				}
				
				try {
					tmpReservierung.doSimpleAssignRoom(rooms.get(row));
				} catch (NotEnoughFreeRoomsException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				} catch (AlreadyCalculatedBelegungenException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
				tmpReservierung.setRechnung(new Rechnung(tmpReservierung));
				tmpReservierung.getRechnung().setDatum(tmpReservierung.getBis());
				tmpReservierung.getRechnung().calculatePreis();
				tmpReservierung.getRechnung().setDatum(new Date());
				price = tmpReservierung.getRechnung().getPreis();
				System.out.println(tmpReservierung.getRechnung().getReason());
			}
			return price;
		}
		public void setRoomLists(List<List<Zimmer>> roomLists){
			rooms = roomLists;
		}
		
		public List<List<Zimmer>> getRoomLists(){
			return rooms;
		}
	};
	
}
